package com.qm.console.component.util;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.*;

/**
 * TimeUtils
 */
public class TimeUtils {
    private final static Logger Log = LoggerFactory.getLogger(TimeUtils.class);
    /**
     * 1秒钟
     */
    public final static long SECOND = 1000;
    /**
     * 1分钟
     */
    public final static long MINUTE = 60 * 1000L;
    /**
     * 1小时
     */
    public final static long HOUR = 60 * MINUTE;
    /**
     * 1天
     */
    public final static long DAY = 24 * HOUR;

    public static final String DATE_FORMAT_DATE_D2 = "yyyy/MM/dd";
    public static final String DATE_FORMAT_DATE_D = "yyyy-MM-dd";
    public static final String DATE_FORMAT_DATE_M = "yyyy-MM-dd HH:mm";
    public static final String DATE_FORMAT_DATE_S = "yyyy-MM-dd HH:mm:ss";

    /**
     * 存放不同的日期模板格式的sdf的Map
     */
    private static Map<String, ThreadLocal<SimpleDateFormat>> sdfMap = new HashMap<String, ThreadLocal<SimpleDateFormat>>();

    static {
        sdfMap.put(DATE_FORMAT_DATE_D, new ThreadLocal() {
            @Override
            protected Object initialValue() {
                return new SimpleDateFormat(DATE_FORMAT_DATE_D);
            }
        });
        sdfMap.put(DATE_FORMAT_DATE_M, new ThreadLocal() {
            @Override
            protected Object initialValue() {
                return new SimpleDateFormat(DATE_FORMAT_DATE_M);
            }
        });
        sdfMap.put(DATE_FORMAT_DATE_S, new ThreadLocal() {
            @Override
            protected Object initialValue() {
                return new SimpleDateFormat(DATE_FORMAT_DATE_S);
            }
        });
        sdfMap.put(DATE_FORMAT_DATE_D2, new ThreadLocal() {
            @Override
            protected Object initialValue() {
                return new SimpleDateFormat(DATE_FORMAT_DATE_D2);
            }
        });
    }

    public static enum DateTypeEnum {
        DAY(1),
        WEEK(2),
        MONTH(3),
        YEAR(4);
        private  Integer timeType;
        DateTypeEnum(Integer timeType){
            this.timeType = timeType;
        }
        @Override
        public String toString(){
            return timeType.toString();
        }

        public Integer value(){
            return timeType;
        }
    }

    private TimeUtils() {
        throw new AssertionError();
    }

    /**
     * long time to string
     *
     * @param timeInMillis
     * @param dateFormat
     * @return
     */
    public static String getTime(long timeInMillis, SimpleDateFormat dateFormat) {
        return dateFormat.format(new Date(timeInMillis));
    }

    /**
     * long time to string
     *
     * @param timeInMillis
     * @param dateFormat
     * @return
     */
    public static String getTime(long timeInMillis, String dateFormat) {
        return sdfMap.get(dateFormat).get().format(new Date(timeInMillis));
    }

    /**
     * @param timeInMillis
     * @return
     */
    public static String getTime(long timeInMillis) {
        return getTime(timeInMillis, DATE_FORMAT_DATE_S);
    }

    /**
     * get current time in milliseconds
     *
     * @return
     */
    public static long getCurrentTimeInLong() {
        return System.currentTimeMillis();
    }

    /**
     * @return
     */
    public static String getCurrentTimeInString() {
        return getTime(getCurrentTimeInLong());
    }

    /**
     * get current time in milliseconds
     *
     * @return
     */
    public static String getCurrentTimeInString(String dateFormat) {
        return getTime(getCurrentTimeInLong(), dateFormat);
    }


    public static Date parse(String time, String format) {
        try {
            return sdfMap.get(format).get().parse(time);
        } catch (ParseException e) {
            e.printStackTrace();
        }

        return null;
    }

    public static Date parse(String time, DateFormat format) {
        try {
            return format.parse(time);
        } catch (ParseException e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 获取当前日期是星期几<br>
     *
     * @param dt
     * @return 当前日期是星期几
     */
    public static String getWeekOfDate(Date dt) {
        String[] weekDays = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};
        Calendar cal = Calendar.getInstance();
        cal.setTime(dt);
        int w = cal.get(Calendar.DAY_OF_WEEK) - 1;
        if (w < 0) {
            w = 0;
        }
        return weekDays[w];
    }

    /**
     * 获取传入日期所在周的起止日期
     *
     * @param timeMilis 日期毫秒值
     * @return {"2018-04-06 00:00:00","2018-04-12 23:59:59}
     */
    public static String[] getWeekStartAndEndTime(long timeMilis) {
        String[] arr = new String[2];
//        String[] weekDays = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};
        Calendar cal = Calendar.getInstance();
        cal.setTime(new Date(timeMilis));
        int w = cal.get(Calendar.DAY_OF_WEEK) - 1;
        if (w == 0)
            w = 7;
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");

        arr[0] = formatter.format(new Date(timeMilis - DAY * (w - 1)));
        arr[1] = formatter.format(new Date(timeMilis + DAY * (7 - w)));

        arr[0] = arr[0] + " 00:00:00";
        arr[1] = arr[1] + " 23:59:59";
        return arr;
    }

    /**
     * 获取传入日期所在月的起止日期
     *
     * @param timeMilis 日期毫秒值
     * @return {"2018-04-01 00:00:00","2018-04-31 23:59:59}
     */
    public static String[] getMonthStartAndEndTime(Long timeMilis,Integer offset) {
        String[] arr = new String[2];
        Calendar cal = Calendar.getInstance();
        cal.setTime(new Date(timeMilis));

        int month = cal.get(Calendar.MONTH)-offset;
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
        int year = cal.get(Calendar.YEAR);
        cal.set(year,month,1);
        int dayOfTthMonth = cal.getActualMaximum(Calendar.DATE);
        arr[0] = formatter.format(cal.getTime());
        cal.set(year,month,dayOfTthMonth);
        arr[1] = formatter.format(cal.getTime());

        arr[0] = arr[0] + " 00:00:00";
        arr[1] = arr[1] + " 23:59:59";
        return arr;
    }

    /**
     * 获取传入日期所在年的起止日期
     *
     * @param timeMilis 日期毫秒值
     * @return {"2018-01-01 00:00:00","2018-12-31 23:59:59}
     */
    public static String[] getYearStartAndEndTime(Long timeMilis,Integer offset) {
        String[] arr = new String[2];
        Calendar cal = Calendar.getInstance();
        cal.setTime(new Date(timeMilis));
        int year = cal.get(Calendar.YEAR)-offset;
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
        cal.set(year-1,12,1);
        arr[0] = formatter.format(cal.getTime());
        cal.set(year,11,31);
        arr[1] = formatter.format(cal.getTime());

        arr[0] = arr[0] + " 00:00:00";
        arr[1] = arr[1] + " 23:59:59";
        return arr;
    }

    /**
     * 获取传入日期所在周的起止日期
     *
     * @param timeMilis 日期毫秒值
     * @return {"2018-04-06 00:00:00","2018-04-06 23:59:59"}
     */
    public static String[] getDayStartAndEndTime(long timeMilis) {
        String[] arr = new String[2];
        Date currentDate = new Date(timeMilis);
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
        arr[0] = formatter.format(currentDate) + " 00:00:00";
        arr[1] = formatter.format(currentDate) + " 23:59:59";
        return arr;
    }

    public static int getBetweenDays(String smallDate, String bigDate, String pattern) {
        return getBetweenDays(parse(smallDate, pattern), parse(bigDate, pattern));
    }

    public static int getBetweenDays(Date smallDate, Date bigDate) {
        int days = (int) ((bigDate.getTime() - smallDate.getTime()) / DAY);
        return days;
    }

    /**
     * 当前时间加减天数
     *
     * @param day 大于0往后推，小于0往前推
     * @return
     */
    public static String addDay(int day, SimpleDateFormat format) {
        long delt = day * DAY;
        long time = System.currentTimeMillis() + delt;
        return format.format(new Date(time));
    }

    /**
     * 当前时间加减天数
     *
     * @param day 大于0往后推，小于0往前推
     * @return
     */
    public static String addDay(int day, String format) {
        return addDay(new Date(), day, format);
    }

    /**
     * 根据给定时间加减天数
     *
     * @param date
     * @param day
     * @param format
     * @return
     */
    public static String addDay(Date date, int day, String format) {
        long delt = day * DAY;
        long time = date.getTime() + delt;
        return sdfMap.get(format).get().format(new Date(time));
    }

    public static void main(String[] args) {
        System.out.println(addDay(-1, TimeUtils.DATE_FORMAT_DATE_M));
    }

    public static Date getCurrentDate() {
        return convertLocalDateToDate(LocalDate.now());
    }

    public static LocalDate getCurrentLocalDate() {
        return LocalDate.now();
    }

    public static Date getDateAfterDays(int days) {
        LocalDate localDate = LocalDate.now();
        localDate = localDate.plusDays(days);

        return convertLocalDateToDate(localDate);
    }

    public static Date getDateBeforeDays(int days) {
        LocalDate localDate = LocalDate.now();
        localDate = localDate.minusDays(days);

        return convertLocalDateToDate(localDate);
    }

    /**
     * convert local date to date
     *
     * @param localDate local date
     * @return date
     */
    public static Date convertLocalDateToDate(LocalDate localDate) {
        ZoneId zone = ZoneId.systemDefault();
        Instant instant = localDate.atStartOfDay().atZone(zone).toInstant();
        return Date.from(instant);
    }

    /**
     * convert date to local date
     *
     * @param date date
     * @return local date
     */
    public static LocalDate convertDateToLocalDate(Date date) {
        Instant instant = date.toInstant();
        ZoneId zoneId = ZoneId.systemDefault();
        return instant.atZone(zoneId).toLocalDate();
    }

    /**
     * get timestamp of local date
     *
     * @param localDate local date
     * @return time
     */
    public static Long getTimestampOfLocalDate(LocalDate localDate) {
        return localDate.atStartOfDay(ZoneId.systemDefault()).toEpochSecond() * 1000;
    }

    /**
     * 获得当前日期与本周日相差的天数
     * @param gmtCreate
     * @return
     */
    private static int getMondayPlus(Date gmtCreate) {
        Calendar cd = Calendar.getInstance();
        cd.setTime(gmtCreate);
        // 获得今天是一周的第几天，星期日是第一天，星期二是第二天......
        // 因为按中国礼拜一作为第一天所以这里减1
        int dayOfWeek = cd.get(Calendar.DAY_OF_WEEK) - 1;
        if (dayOfWeek == 1) {
            return 0;
        } else {
            return 1 - dayOfWeek;
        }
    }

    /**
     * 获得下周星期一的日期
     * @param gmtCreate
     * @return
     */
    public static Date getNextMonday(Date gmtCreate) {
        int mondayPlus = getMondayPlus(gmtCreate);
        GregorianCalendar currentDate = new GregorianCalendar();
        currentDate.add(GregorianCalendar.DATE, mondayPlus + 7);
        Date monday = currentDate.getTime();
        return monday;
    }

    /**
     * 设置当前日期的时分秒毫秒
     * @param date
     * @param hourOfDay
     * @param minute
     * @param second
     * @param ms
     * @return
     */
    public static Date setTime( final Date date, final int hourOfDay, final int minute, final int second, final int ms ) {
        final GregorianCalendar gc = new GregorianCalendar();
        gc.setTime( date );
        gc.set( Calendar.HOUR_OF_DAY, hourOfDay );
        gc.set( Calendar.MINUTE, minute );
        gc.set( Calendar.SECOND, second );
        gc.set( Calendar.MILLISECOND, ms );
        return gc.getTime();
    }

    /**
     * 计算两个日期之间的天数差，不是时间间隔
     * @param date1
     * @param date2
     * @return
     */
    public static int differentDays(Date date1, Date date2)
    {
        Calendar cal1 = Calendar.getInstance();
        cal1.setTime(date1);

        Calendar cal2 = Calendar.getInstance();
        cal2.setTime(date2);
        int day1= cal1.get(Calendar.DAY_OF_YEAR);
        int day2 = cal2.get(Calendar.DAY_OF_YEAR);

        int year1 = cal1.get(Calendar.YEAR);
        int year2 = cal2.get(Calendar.YEAR);
        int distance;
        if(year1 != year2)   //同一年
        {
            int timeDistance = 0 ;
            for(int i = year1 ; i < year2 ; i ++)
            {
                if(i%4==0 && i%100!=0 || i%400==0)    //闰年
                {
                    timeDistance += 366;
                }
                else    //不是闰年
                {
                    timeDistance += 365;
                }
            }
            distance = timeDistance + (day2-day1) ;
        }
        else    //不同年
        {
            distance = day2-day1;
        }
        Log.info("[differentDays] days:{}", distance);
        return distance;
    }

    /**
     * 计算两个日期是否一前一后
     * @param updateTime
     * @return
     */
    public static boolean checkTwoDateOrder(List<String> updateTime){
        if(updateTime==null||updateTime.size()!=2){
            return false;
        }
        Date startTime;
        Date endTime;
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        startTime = dateFormat.parse(updateTime.get(0),new ParsePosition(0));
        endTime = dateFormat.parse(updateTime.get(1),new ParsePosition(0));
        if(startTime.after(endTime)){
            return false;
        }
        return true;
    }
    /**
     * 分表用，获取year,month
     * @param updateTime
     * @return
     */
    public static int[] parseUpdateTime(String[] updateTime){
        int[] pTime = new int[4];
        Calendar cal = Calendar.getInstance();
        if(updateTime == null){
            pTime[0] = cal.get(Calendar.YEAR); //startYear
            pTime[1] = cal.get(Calendar.MONTH); //startMonth
            pTime[2] = pTime[0]; //endYear
            pTime[3] = pTime[1]; //endYear
        }else {
            pTime[0] = Integer.parseInt(updateTime[0].substring(0,4));
            pTime[1] = Integer.parseInt(updateTime[0].substring(5,7));
            pTime[2] = Integer.parseInt(updateTime[1].substring(0,4));
            pTime[3] = Integer.parseInt(updateTime[1].substring(5,7));
        }
        return pTime;
    }

    public static Date parseUpdateTimeToDate(String updateTime){
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date result = new Date();
        result = dateFormat.parse(updateTime,new ParsePosition(0));
        return result;
    }

    /**
     * 获取当前日期是一周中的第几天
     * @return
     */
    public static int getDayOfWeek(){
        Calendar calendar = Calendar.getInstance();
        int day = calendar.get(Calendar.DAY_OF_WEEK);
        if(day == 1){
            return 7;
        }else {
            return day-1;
        }
    }

    /**
     * @param currentTime
     * @param dateTypeEnum
     * @param offset
     * @return 根据条件返回起始和结束时间
     */
    public static Date[] getStartAndEndTime(Date currentTime, DateTypeEnum dateTypeEnum, Integer offset){
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Integer daysOfAWeek = 7;
        Long dayMs =  24*60*60*1000L;
        Long currentTimeMills = currentTime.getTime();
        Date[] result = new Date[2];
        String[] days;
        Long millsOfTargetDay;
        switch (dateTypeEnum){
            case DAY:
                millsOfTargetDay = currentTimeMills - offset*dayMs;
                days = getDayStartAndEndTime(millsOfTargetDay);
                break;
            case WEEK:
                millsOfTargetDay = currentTimeMills - offset*daysOfAWeek*dayMs;
                days = getWeekStartAndEndTime(millsOfTargetDay);
                break;
            case MONTH:
                millsOfTargetDay = currentTimeMills;
                days = getMonthStartAndEndTime(millsOfTargetDay,offset);
                break;
            case YEAR:
                millsOfTargetDay = currentTimeMills;
                days = getYearStartAndEndTime(millsOfTargetDay,offset);
                break;
            default:
                millsOfTargetDay = currentTimeMills - offset*dayMs;
                days = getDayStartAndEndTime(millsOfTargetDay);
                break;
        }
        result[0] = simpleDateFormat.parse(days[0],new ParsePosition(0));
        result[1] = simpleDateFormat.parse(days[1],new ParsePosition(0));
        return result;
    }

    // 获取本周的开始时间
    public static Date getBeginDayOfWeek() {
        Date date = new Date();
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        int dayofweek = cal.get(Calendar.DAY_OF_WEEK);
        if (dayofweek == 1) {
            dayofweek += 7;
        }
        cal.add(Calendar.DATE, 2 - dayofweek);
        return getDayStartTime(cal.getTime());
    }

    // 获取本周的结束时间
    public static Date getEndDayOfWeek() {
        Calendar cal = Calendar.getInstance();
        cal.setTime(getBeginDayOfWeek());
        cal.add(Calendar.DAY_OF_WEEK, 6);
        Date weekEndSta = cal.getTime();
        return getDayEndTime(weekEndSta);
    }

    // 获取上周的开始时间
    public static Date getBeginDayOfLastWeek() {
        Date date = new Date();
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        int dayofweek = cal.get(Calendar.DAY_OF_WEEK);
        if (dayofweek == 1) {
            dayofweek += 7;
        }
        cal.add(Calendar.DATE, 2 - dayofweek - 7);
        return getDayStartTime(cal.getTime());
    }

    // 获取上周的结束时间
    public static Date getEndDayOfLastWeek() {
        Calendar cal = Calendar.getInstance();
        cal.setTime(getBeginDayOfLastWeek());
        cal.add(Calendar.DAY_OF_WEEK, 6);
        Date weekEndSta = cal.getTime();
        return getDayEndTime(weekEndSta);
    }

    // 获取某个日期的开始时间
    public static Timestamp getDayStartTime(Date d) {
        Calendar calendar = Calendar.getInstance();
        if (null != d)
            calendar.setTime(d);
        calendar.set(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH),
                calendar.get(Calendar.DAY_OF_MONTH), 0, 0, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return new Timestamp(calendar.getTimeInMillis());
    }

    // 获取某个日期的结束时间
    public static Timestamp getDayEndTime(Date d) {
        Calendar calendar = Calendar.getInstance();
        if (null != d)
            calendar.setTime(d);
        calendar.set(calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH),
                calendar.get(Calendar.DAY_OF_MONTH), 23, 59, 59);
        calendar.set(Calendar.MILLISECOND, 999);
        return new Timestamp(calendar.getTimeInMillis());
    }

    public static String formatDate(Date date) {
        String temp_str = "";
        SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT_DATE_D);
        temp_str = sdf.format(date);
        return temp_str;
    }

}
